using System.ComponentModel.DataAnnotations.Schema;

namespace CollabApp.Domain.ValueObjects
{
    /// <summary>
    /// 领地值对象 - 表示一块封闭的领地区域
    /// </summary>
    [ComplexType]
    public class Territory
    {
        public List<Position> Boundary { get; private set; }
        public double Area { get; private set; }
        public Position Center { get; private set; }
        public DateTime CreatedAt { get; private set; }

        public Territory(List<Position> boundary)
        {
            if (boundary.Count < 3)
                throw new ArgumentException("领地边界至少需要3个点");

            Boundary = boundary.ToList();
            Area = CalculateArea();
            Center = CalculateCenter();
            CreatedAt = DateTime.UtcNow;
        }

        /// <summary>
        /// 计算多边形面积（使用鞋带公式）
        /// </summary>
        /// <returns>面积</returns>
        private double CalculateArea()
        {
            if (Boundary.Count < 3) return 0;

            double area = 0;
            for (int i = 0; i < Boundary.Count; i++)
            {
                var current = Boundary[i];
                var next = Boundary[(i + 1) % Boundary.Count];
                area += (current.X * next.Y - next.X * current.Y);
            }
            return Math.Abs(area) / 2.0;
        }

        /// <summary>
        /// 计算多边形中心点
        /// </summary>
        /// <returns>中心点</returns>
        private Position CalculateCenter()
        {
            if (Boundary.Count == 0) return new Position(0, 0);

            float centerX = Boundary.Average(p => p.X);
            float centerY = Boundary.Average(p => p.Y);
            return new Position(centerX, centerY);
        }

        /// <summary>
        /// 检查点是否在领地内（射线投射算法）
        /// </summary>
        /// <param name="point">要检查的点</param>
        /// <returns>是否在领地内</returns>
        public bool Contains(Position point)
        {
            if (Boundary.Count < 3) return false;

            int intersectionCount = 0;
            for (int i = 0; i < Boundary.Count; i++)
            {
                var current = Boundary[i];
                var next = Boundary[(i + 1) % Boundary.Count];

                // 检查射线是否与边相交
                if (IsRayIntersectingSegment(point, current, next))
                {
                    intersectionCount++;
                }
            }

            return intersectionCount % 2 == 1;
        }

        /// <summary>
        /// 检查从点向右的射线是否与线段相交
        /// </summary>
        private bool IsRayIntersectingSegment(Position point, Position p1, Position p2)
        {
            // 确保p1在p2下方
            if (p1.Y > p2.Y)
            {
                (p1, p2) = (p2, p1);
            }

            // 射线不在线段Y范围内
            if (point.Y < p1.Y || point.Y >= p2.Y)
                return false;

            // 线段是水平的
            if (Math.Abs(p1.Y - p2.Y) < 0.001f)
                return false;

            // 计算射线与线段的交点X坐标
            float intersectionX = p1.X + (point.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y);
            
            return intersectionX > point.X;
        }

        /// <summary>
        /// 检查是否与另一个领地重叠
        /// </summary>
        /// <param name="other">另一个领地</param>
        /// <returns>是否重叠</returns>
        public bool OverlapsWith(Territory other)
        {
            // 检查边界点是否在对方领地内
            foreach (var point in Boundary)
            {
                if (other.Contains(point))
                    return true;
            }

            foreach (var point in other.Boundary)
            {
                if (Contains(point))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// 获取被包围的面积（用于"吞噬"计算）
        /// </summary>
        /// <param name="other">被包围的领地</param>
        /// <returns>被包围的面积</returns>
        public double GetEngulfedArea(Territory other)
        {
            if (!IsCompletelyEngulfed(other))
                return 0;

            return other.Area;
        }

        /// <summary>
        /// 检查是否完全包围另一个领地
        /// </summary>
        /// <param name="other">另一个领地</param>
        /// <returns>是否完全包围</returns>
        public bool IsCompletelyEngulfed(Territory other)
        {
            return other.Boundary.All(point => Contains(point));
        }

        public override bool Equals(object? obj)
        {
            if (obj is not Territory other) return false;
            return Boundary.SequenceEqual(other.Boundary);
        }

        public override int GetHashCode()
        {
            return Boundary.Aggregate(0, (hash, pos) => hash ^ pos.GetHashCode());
        }
    }
}